#include "rtx.h"
#include "user_processes.h"
#include "system_processes.h"
#include "string.h"
#include "processes.h"

VOID set_priority_process (VOID)
{
	MESSAGE *msg = NULL;
	UINT32 process = 0, priority = 0;
	PCB* new_pcb = NULL;

	register_kcd('C');

	while(1)
	{
		int sender = 0;
		msg = (MESSAGE *)receive_message(&sender);

		if(msg != NULL)
		{
			if(msg->sender_process_ID == KCD_PROCESS)
			{
				process = char_to_int(msg->message_content[2]);
				if(process == 0)
					process = char_to_int(msg->message_content[3]);
				else process = (process * 10) + char_to_int(msg->message_content[3]);
				priority = char_to_int(msg->message_content[6]);

				if(((process > 0) && (process < 21)) && (priority < 4))
				{
					new_pcb = get_PCB(process);
					set_process_priority(process, priority);
				}
				else
				{
					MESSAGE *error = (MESSAGE *)request_memory_block();
					error->sender_process_ID = SET_PRIO_PROCESS;
					error->destination_process_ID = CRT_PROCESS;
					error->message_type = DISPLAY_TEXT;
					error->message_content[0] = 'E';
					error->message_content[1] = 'R';
					error->message_content[2] = 'R';
					error->message_content[3] = 'O';
					error->message_content[4] = 'R';
					error->message_content[5] = '!';
					send_message(CRT_PROCESS, error);
					release_memory_block(error);
				}
			}
		}
		release_memory_block(msg);
		release_processor();
	}
}

VOID wall_clock_process (VOID)
{
	MESSAGE *msg = NULL, *output = NULL;
	UINT32 hour = 0;
	UINT32 minute = 0;
	UINT32 second = 0;
	BOOLEAN on = FALSE;

	register_kcd('W');

	while(1)
	{
		int sender = 0;
		msg = (MESSAGE *)receive_message(&sender);

		if(msg != NULL)
		{
			if(msg->sender_process_ID == KCD_PROCESS && msg->message_content[1] == 'S')
			{
				// Just assume they entered the time properly
				hour = char_to_int(msg->message_content[2]);
				hour = (hour * 10) + char_to_int(msg->message_content[3]);

				minute = char_to_int(msg->message_content[5]);
				minute = (minute * 10) + char_to_int(msg->message_content[6]);

				second = char_to_int(msg->message_content[8]);
				second = (second * 10) + char_to_int(msg->message_content[9]);

				if(((hour >= 0) && (hour < 24)) && ((minute >= 0) && (minute < 60)) && ((second >= 0) && (second < 60)))
				{
					on = TRUE;
					output = (MESSAGE *)request_memory_block();
					output->message_type = 1;
					output->sender_process_ID = CLOCK_PROCESS;
					output->destination_process_ID = CRT_PROCESS;
					output->message_content[0] = msg->message_content[2];
					output->message_content[1] = msg->message_content[3];
					output->message_content[2] = msg->message_content[4];
					output->message_content[3] = msg->message_content[5];
					output->message_content[4] = msg->message_content[6];
					output->message_content[5] = msg->message_content[7];
					output->message_content[6] = msg->message_content[8];
					output->message_content[7] = msg->message_content[9];
					output->message_content[8] = '\r';
					output->message_content[9] = '\0';
					send_message(CRT_PROCESS, output);

					// Send this current process another message so it can update in 10 seconds
					output = (MESSAGE *)request_memory_block();
					output->message_type = 1;
					output->sender_process_ID = CLOCK_PROCESS;
					output->destination_process_ID = current_process->process_ID;
					delayed_send(current_process->process_ID, output, 100);
				}
			}
			else if(msg->sender_process_ID == KCD_PROCESS && msg->message_content[1] == 'T')
			{
				on = FALSE;
			}
			else if(msg->sender_process_ID == TIMER_I_PROCESS && on)
			{
				// Increment the clock
				second++;
				if(second >= 60)
				{
					second = 0;
					minute++;
					if(minute >= 60)
					{
						minute = 0;
						hour++;
						if(hour >= 24)
							hour = 0;
					}
				}

				output = (MESSAGE *)request_memory_block();
				output->message_type = 1;
				output->sender_process_ID = CLOCK_PROCESS;
				output->destination_process_ID = CRT_PROCESS;
				output->message_content[0] = int_to_string(hour/10);
				output->message_content[1] = int_to_string(hour%10);
				output->message_content[2] = ':'
				output->message_content[3] = int_to_string(minute/10);
				output->message_content[4] = int_to_string(minute%10);
				output->message_content[5] = ':'
				output->message_content[6] = int_to_string(second/10);
				output->message_content[7] = int_to_string(second%10);
				output->message_content[8] = '\r';
				output->message_content[9] = '\0';
				send_message(CRT_PROCESS, output);

				// Send this current process another message so it can update in 10 seconds
				output = (MESSAGE *)request_memory_block();
				output->message_type = 1;
				output->sender_process_ID = CLOCK_PROCESS;
				output->destination_process_ID = current_process->process_ID;
				delayed_send(current_process->process_ID, output, 100);
			}
		}

		release_memory_block(msg);
		release_processor();
	}
}

VOID process_a_1 (VOID)
{
	MESSAGE *mem = NULL;
	UINT32 num = 0;

	// mem = (MESSAGE *)request_memory_block();
	register_kcd('Z');

	while(1)
	{
		int sender = 0;
		mem = (MESSAGE *)receive_message(&sender);
		if(mem->message_content[0] == 'Z')
		{
			release_memory_block(mem);
			break;
		}
		else release_memory_block(mem);
	}

	while(1)
	{
		mem = (MESSAGE *)request_memory_block();
		mem->message_type = COUNT_REPORT;
		mem->message_content = num;
		send_message(PROCESS_B_1, mem);
		num = num + 1;
		release_processor();
	}
}

VOID process_a_2 (VOID)
{
	MESSAGE *mem = NULL;
	UINT32 num = 0;

	// mem = (MESSAGE *)request_memory_block();
	register_kcd('Y');

	while(1)
	{
		int sender = 0;
		mem = (MESSAGE *)receive_message(&sender);
		if(mem->message_content[0] == 'Y')
		{
			release_memory_block(mem);
			break;
		}
		else release_memory_block(mem);
	}

	while(1)
	{
		mem = (MESSAGE *)request_memory_block();
		mem->message_type = COUNT_REPORT;
		mem->message_content = num;
		send_message(PROCESS_B_2, mem);
		num = num + 1;
		release_processor();
	}
}

VOID process_a_3 (VOID)
{
	MESSAGE *mem = NULL;
	UINT32 num = 0;

	// mem = (MESSAGE *)request_memory_block();
	register_kcd('X');

	while(1)
	{
		int sender = 0;
		mem = (MESSAGE *)receive_message(&sender);
		if(mem->data[0] == 'X')
		{
			release_memory_block(mem);
			break;
		}
		else release_memory_block(mem);
	}

	while(1)
	{
		mem = (MESSAGE *)request_memory_block();
		mem->message_type = COUNT_REPORT;
		mem->message_content = num;
		send_message(PROCESS_B_2, mem);
		num = num + 1;
		release_processor();
	}
}

VOID process_b_1 (VOID)
{
	MESSAGE *msg = NULL;

	while(1)
	{
		int sender = 0;
		msg = (MESSAGE *)receive_message(&sender);
		send_message(PROCESS_C_1, msg);
	}
}

VOID process_b_2 (VOID)
{
	MESSAGE *msg = NULL;

	while(1)
	{
		int sender = 0;
		msg = (MESSAGE *)receive_message(&sender);
		send_message(PROCESS_C_2, msg);
	}
}

VOID process_c_1 (VOID)
{
	PCB *this_pcb = get_pcb(PROCESS_C_1);
	MESSAGE *msg = NULL, *mem = NULL;
	UINT32 num = 0;

	while(1)
	{
		int sender = 0;

		if(this_pcb->message_queue_head == NULL)
			msg = (MESSAGE *)receive_message(&sender);
		else msg = (MESSAGE *)dequeue_message(this_pcb);

		if(msg->message_type == COUNT_REPORT)
		{
			num = (UINT32) msg->message_content;

			if((num % 20) == 0)
			{
				mem = (MESSAGE *)request_memory_block();
				mem->message_type = WAKEUP10;
				// INSERT COMMAND TO PRINT TO THE UART HERE
				delayed_send(PROCESS_C_1, mem, 1000);
				while(1)
				{
					msg = (MESSAGE *)receive_message(&sender);
					if(msg->message_type == WAKEUP10)
						break;
					else enqueue_message(this_pcb, msg);
				}
			}
		}
		release_memory_block(msg);
		release_processor();
	}
}

VOID process_c_2 (VOID)
{
	PCB *this_pcb = get_PCB(PROCESS_C_2);
	MESSAGE *msg = NULL, *mem = NULL;
	BOOLEAN free = FALSE;
	UINT32 num = 0;

	mem = (MESSAGE *)request_memory_block();

	while(1)
	{
		int sender = 0;

		if(this_pcb->message_queue_head == NULL)
			msg = (MESSAGE *)receive_message(&sender);
		else msg = (MESSAGE *)dequeue_message(this_pcb);

		free = TRUE;

		if(msg->message_type == COUNT_REPORT)
		{
			num = (UINT32) msg->message_content;
			// INSERT COMMAND TO PRINT TO THE CRT HERE
			free = FALSE;
			mem->message_type = WAKEUP10;
			delayed_send(PROCESS_C_2, mem, 1000);
			while(1)
			{
				mem = (MESSAGE *)receive_message(&sender);
				if(mem->message_type == WAKEUP10)
					break;
				else enqueue_message(this_pcb, mem);
			}
		}
		if(free == TRUE)
			release_memory_block(msg);
		release_processor();
	}
}
